#include "cost_classes.h"

#include <algorithm>
#include <stdexcept>

using namespace std;

CostChanger::CostChanger(int _cost) : cost(_cost) {
	if ( cost < 0 ) throw invalid_argument("CostChanger::CostChanger(int)");
}

CostChanger::~CostChanger() {
	// All people watching me
	while ( !observerCollection.empty() )
		// Don't watch me anymore
		unregisterObserver(*observerCollection.begin());

	// All people I'm watching
	while ( !observedCollection.empty() )
		// I won't watch them anymore
		(*observedCollection.begin())->unregisterObserver(this);
}

CostKeeper::CostKeeper(int _cost) : CostChanger(_cost) {}

int CostChanger::getCost() const { return cost; }

void CostChanger::registerObserver( CostChanger * _obs ) {
	if ( !_obs )
		throw invalid_argument("CostChanger::registerObserver(CostChanger*)");
	if ( _obs == this )
		throw runtime_error("CostChanger::registerObserver(CostChanger*)");

	observerCollection.push_back(_obs);

	_obs->addCost(cost);
	_obs->registerObserved(this);
}

// Internally called
void CostChanger::registerObserved( CostChanger * _obs ) {
	observedCollection.push_back(_obs);
}

void CostChanger::unregisterObserver( CostChanger * _obs ) {
	auto it = find(begin(observerCollection), end(observerCollection), _obs);

	if ( it == observerCollection.end() )
		throw runtime_error("CostChanger::unregisterObserver(CostChanger*)");

	observerCollection.erase(it);

	_obs->addCost(-cost);
	_obs->unregisterObserved(this);
}

void CostChanger::unregisterObserved( CostChanger * _obs ) {
	auto it = find(begin(observedCollection), end(observedCollection), _obs);

	if ( it == observedCollection.end() )
		throw runtime_error("CostChanger::unregisterObserved(CostChanger*)");

	observedCollection.erase(it);
}

void CostChanger::setCost( int _cost ) {
	int diffCost = _cost - cost;

	addCost(diffCost);
}

void CostChanger::addCost(int _diffCost) {
	cost += _diffCost;

	notifyObservers(_diffCost);
}

void CostChanger::notifyObservers(int _costDiff) {
	for ( auto & it : observerCollection )
		it->notify(_costDiff);
}

void CostChanger::notify(int _costDiff) {
	cost += _costDiff;

	notifyObservers(_costDiff);
}

